home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastep6.asm < prev    next >
Assembly Source File  |  1995-01-01  |  16KB  |  403 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play 8bit DMA mode for SoundBlaster v2.00
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16/SBPRO2/SB2.0, TESTPHASE ON SB1.0
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, DOS 1.0, SoundBlaster (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / 4..44khz (highspeed DMA)
  9. ; Parameters  : none
  10. ; Notes:
  11. ;     ■ pause/continue does only work on SB16 models because of highspeed DMA
  12. ;     ■ To creat a 8 bit mono unsigned file do :   "VOC2RAW TEST1.VOC /I"
  13. ;
  14. ; ■ DSP command 48h  ... set DMA block size
  15. ; ■ DSP command 90h  ... play highspeed DMA 8bit mono autoinit
  16. ; ■ DSP command 40h  ... set sample rate
  17. ; ■ DSP command D1h  ... Enable Speaker
  18. ; ■ DSP command D3h  ... Disable Speaker
  19. ; ■ DSP command D0h  ... Halt Autoinit 8 bit DMA operation
  20. ; ■ DSP command D4h  ... Continue Autoinit 8 bit DMA operation
  21. ;
  22.  
  23. .MODEL small
  24. .286
  25.  
  26. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  27.  
  28. ; SoundBlaster SETUP
  29. BASEADDR           EQU 0220h       ;SoundBlaster base address
  30. IRQ7               EQU 15          ;SoundBlaster IRQ
  31. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  32.  
  33. ; PIC MASKS FOR MASK/DEMASK IRQ
  34. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  35. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  36.  
  37. ; DMA CONTROLLER REGISTERS :
  38. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  39. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  40. CLEARFLIPFLOP      EQU 00ch
  41. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  42. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMA 1
  43. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  44.  
  45. ; SAMPLERATE : (if you change it pay attention to maximum samplerate)
  46. TIMECONST          EQU 165          ; = 10989 Hz (256-1000000/11000)
  47.  
  48. ; DMA WRITE MODE
  49. WANTEDMODE         EQU 01011000b    ; singlemode, autoinit, readmode
  50.  
  51. ;──────────────────────────────────────────────────────────────────────────────
  52. ; MACRO DEFINITIONs
  53. ;──────────────────────────────────────────────────────────────────────────────
  54. STARTUP                 MACRO
  55. ; MASM 5.x COMPATIBILITY
  56. __start:                mov     ax,DGROUP
  57.                         mov     ds,ax
  58.                         mov     bx,ss
  59.                         sub     bx,ax
  60.                         shl     bx,004h
  61.                         mov     ss,ax
  62.                         add     sp,bx
  63. ENDM
  64.  
  65. WAITWRITE               MACRO
  66. LOCAL                   loopWait,endloop
  67. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  68. ;          Returns   : n/a
  69. ;          Destroys  : AL
  70.  
  71.                         push    cx
  72.                         xor     cx,cx           ; need that for slow SBs !
  73. loopWait:               dec     cx
  74.                         jz      endloop
  75.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  76.                         or      al,al
  77.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  78. endloop:                pop     cx
  79. ENDM
  80.  
  81. WAITREAD                MACRO
  82. LOCAL                   loopWait,endloop
  83. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  84. ;          Returns   : n/a
  85. ;          Destroys  : AL
  86.  
  87.                         push    cx
  88.                         xor     cx,cx           ; need that for slow SBs !
  89. loopWait:               dec     cx
  90.                         jz      endloop
  91.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  92.                         or      al,al
  93.                         jns     loopWait        ; Jump if bit7=0 - no data available
  94. endloop:                pop     cx
  95. ENDM
  96.  
  97. RESET_DSP               MACRO
  98. local                   SBthere
  99. ;          Arguments : n/a
  100. ;          Returns   : n/a
  101. ;          Destroys  : DX,AL
  102.  
  103.                         mov      dx,BASEADDR+06h
  104.                         mov      al,1
  105.                         out      dx,al          ; start DSP reset
  106.  
  107.                         in       al,dx
  108.                         in       al,dx
  109.                         in       al,dx
  110.                         in       al,dx          ; wait 3 µsec
  111.  
  112.                         xor      al,al
  113.                         out      dx,al          ; end DSP Reset
  114.  
  115.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  116.                         WAITREAD
  117.                         sub      dx,4           ; dx = DSP Read Data
  118.                         in       al,dx
  119.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  120.                         je       SBthere
  121.                         jmp      RESET_ERROR    ; No SB - exit program
  122. SBthere:
  123. ENDM
  124. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  125.  
  126. .STACK 100h
  127.  
  128. .DATA
  129. ;──────────────────────────────────────────────────────────────────────────────
  130. ; TWO COPIES FOR PAGE OVERRIDE REASONS :
  131.  
  132. SAMPLEBUFFER LABEL BYTE
  133.     INCLUDE TEST1.INC         ; FIRST COPY OF SAMPLE SOUND
  134. SAMPLEBUFFEREND LABEL BYTE
  135.     INCLUDE TEST1.INC         ; SECOND COPY OF SAMPLE SOUND
  136.  
  137.     part                db 1
  138.  
  139.     information         db 13,10,'DMASTEP6.EXE - DMA autoinit highspeed mode'
  140.                         db 13,10,'Pause playing with key "p" and continue it then with any key (SB16+).'
  141.                         db 13,10,'Stop playing with <ESC>.','$'
  142.     txtpart0            db 13,10,'playing part 0','$'
  143.     txtpart1            db 13,10,'playing part 1','$'
  144.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  145.  
  146.     OLDInterruptSEG     dw ?
  147.     OLDInterruptOFS     dw ?
  148.  
  149.     positionLo          db ?
  150.     positionHi          db ?
  151.  
  152.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  153. ;──────────────────────────────────────────────────────────────────────────────
  154. .CODE
  155.  STARTUP
  156.  
  157.            RESET_DSP
  158.  
  159.            ; WRITE INFORMATION TO SCREEN :
  160.            mov     dx,offset information
  161.            mov     ah,9
  162.            int     21h                         ; write program information to screen
  163.  
  164.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  165.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  166.            WAITWRITE
  167.            mov     al,0D1h                     ; AL = Enable speaker
  168.            out     dx,al                       ; Output: DSP Write Data or Command
  169.  
  170.            ; SETUP IRQ :
  171.            xor     ax,ax
  172.            mov     es,ax                       ; es to page 0 (Interrupt table)
  173.            mov     si,IRQ7*4                   ; si = position in interrupt table
  174.  
  175.            ; DISABLE IRQ
  176.            in      al,021h
  177.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  178.            out     021h,al
  179.  
  180.            ; CHANGE POINTER IN INTERRUPT TABLE
  181.            mov     ax,es:[si]
  182.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  183.            mov     ax,OFFSET OWN_IRQ
  184.            mov     es:[si],ax                  ; set offset of new interrupt routine
  185.            mov     ax,es:[si+2]
  186.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  187.            mov     ax,cs
  188.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  189.  
  190.            ; CHANGE PIC MASK :
  191.            in      al,021h
  192.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  193.            out     021h,al
  194.  
  195.            mov     si,offset samplebuffer
  196.            mov     cx,SAMPLEBUFFERLENGTH-1
  197.  
  198. ;──────────────────────────────────────────────────────────────────────────────
  199. ; calculate page and offset for DMAcontroller :
  200. ;
  201. ; segment*16+offset - 20bit memory location -> upper 4 bits  = page
  202. ;                                              lower 16 bits = offset
  203. ;──────────────────────────────────────────────────────────────────────────────
  204.            mov     si,offset samplebuffer
  205.            mov     cx,SAMPLEBUFFERLENGTH-1
  206.  
  207.            mov     ax,ds
  208.            rol     ax,4                ; * 16 - higher 4 bits in al
  209.            mov     bl,al
  210.            and     bl,00fh             ; BL - higher 4 bits
  211.            and     al,0f0h             ; clear higher 4bits in AL
  212.            add     si,ax               ; SI = offset
  213.            adc     bl,0                ; BL = page
  214. ;──────────────────────────────────────────────────────────────────────────────
  215. ; check for DMApage override :
  216. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  217. ; transfer data is placed in one page - no page overrides are allowed
  218. ;──────────────────────────────────────────────────────────────────────────────
  219. ; To solve that :
  220. ; creat a DMA buffer with double size you want - if the first part is placed
  221. ; on a page border the second part is for sure not
  222. ;──────────────────────────────────────────────────────────────────────────────
  223.            neg     si          ; si = 65536 - si   (bytes left to DMA page border)
  224.            cmp     si,cx       ; if si (bytes left to border) > cx (bytes to play)
  225.            ja      nooverride  ; then there's no page override
  226.  
  227.            ; WE HAVE TO USE SECOND PART
  228.            neg     si          ; si = offset of first part
  229.            add     si,cx       ; si = si + length of one part
  230.            inc     si          ; si=si+1 - start of second part
  231.            inc     bl          ; second part is then on the next page
  232.            neg     si          ; look at the next command ;)
  233.                                ; (that is better than a jump ?)
  234. nooverride:
  235.            neg     si
  236.  
  237. ;──────────────────────────────────────────────────────────────────────────────
  238. ; Setup DMA-controller :
  239. ;
  240. ; 1st  MASK DMA CHANNEL
  241. ;
  242.            mov     al,DMAchannel
  243.            add     al,4
  244.            out     WRITEMASK,al
  245. ;──────────────────────────────────────────────────────────────────────────────
  246. ; 2nd  CLEAR FLIPFLOP
  247. ;
  248.            out     CLEARFLIPFLOP,al
  249. ;──────────────────────────────────────────────────────────────────────────────
  250. ; 3rd  WRITE TRANSFER MODE
  251. ;
  252.            mov     al,WANTEDMODE
  253.            add     al,DMAchannel
  254.            out     WRITEMODE,al
  255. ;──────────────────────────────────────────────────────────────────────────────
  256. ; 4th  WRITE PAGE NUMBER
  257. ;
  258.            mov     al,bl
  259.            out     PAGE_CHN,al
  260. ;──────────────────────────────────────────────────────────────────────────────
  261. ; 5th  WRITE BASEADDRESS
  262. ;
  263.            mov     ax,si
  264.            out     BASE_CHN,al
  265.            mov     al,ah
  266.            out     BASE_CHN,al
  267. ;──────────────────────────────────────────────────────────────────────────────
  268. ; 6th  WRITE SAMPLELENGTH-1
  269. ;
  270.            mov     al,cl
  271.            out     COUNT_CHN,al
  272.            mov     al,ch
  273.            out     COUNT_CHN,al
  274. ;──────────────────────────────────────────────────────────────────────────────
  275. ; 7th  DEMASK CHANNEL
  276. ;
  277.            mov     al,DMAchannel
  278.            out     WRITEMASK,al
  279.  
  280. ;──────────────────────────────────────────────────────────────────────────────
  281. ; Setup SoundBlaster :
  282. ;
  283. ; 1st  SET TIMECONSTANT
  284. ;
  285.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  286.            WAITWRITE
  287.            mov     al,040h                     ;AL = Set timeconstant
  288.            out     dx,al
  289.            WAITWRITE
  290.            mov     al,TIMECONST
  291.            out     dx,al
  292.  
  293. ;──────────────────────────────────────────────────────────────────────────────
  294. ; 2nd  Use highspeed autoinit mode (DSPC 90h)
  295. ;
  296.            ;SETUP SIZE
  297.            WAITWRITE
  298.            mov     al,048h                     ;AL = DMA DAC 8bit
  299.            out     dx,al
  300.            mov     cx,SAMPLEBUFFERLENGTH
  301.            shr     cx,1                        ; generate IRQ every half buffer
  302.            dec     cx
  303.            WAITWRITE
  304.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  305.            out     dx,al
  306.            WAITWRITE
  307.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  308.            out     dx,al
  309.  
  310.            ; SETUP PLAYMODE
  311.            WAITWRITE
  312.            mov     al,90h
  313.            out     dx,al
  314.  
  315. ; NOW TRANSFER ..... :)
  316.  
  317. waitloop:  mov     ah,01                       ;AH = Check for character function
  318.            int     016h                        ;   Interrupt: Keyboard
  319.            jz      waitloop                    ; wait for a key (sound in background)
  320.            xor     ah,ah                       ;Read character, flush keypress
  321.            int     016h                        ;   Interrupt: Keyboard
  322.            cmp     al,'p'                      ; check for pause key
  323.            je      pause                       ; ok
  324.            cmp     al,27
  325.            jne     waitloop
  326.            jmp     exit                        ; other key so stop playing
  327. pause:     ; NOW HALT PLAYING :
  328.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  329.            WAITWRITE
  330.            mov     al,0D0h
  331.            out     dx,al
  332.  
  333.            xor     ah,ah                       ;Read character, flush keypress
  334.            int     016h                        ;   Interrupt: Keyboard
  335.  
  336.            ; NOW CONTINUE PLAYING
  337.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  338.            WAITWRITE
  339.            mov     al,0D4h
  340.            out     dx,al
  341.  
  342.            jmp     waitloop
  343.  
  344. exit:      RESET_DSP
  345.  
  346.            ; RESTORE PIC MASK
  347.            in      al,021h
  348.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  349.            out     021h,al
  350.  
  351.            ; RESTORE IRQ :
  352.            xor     ax,ax
  353.            mov     es,ax                       ; es to page 0 (Interrupt table)
  354.            mov     si,IRQ7*4
  355.            mov     ax,[OLDInterruptOFS]
  356.            mov     es:[si],ax                  ; set old interrupt routine
  357.            mov     ax,[OLDInterruptSEG]
  358.            mov     es:[si+2],ax
  359.  
  360.            ; CLEAR KEYBUFFER
  361.            mov     ah,01
  362.            int     16h
  363.            jz      return2dos
  364.            xor     ah,ah                       ;Read character, flush keypress
  365.            int     016h                        ;   Interrupt: Keyboard
  366.  
  367.            ; TERMINATE EXE:
  368. return2dos:
  369.            mov     ax,04c00h
  370.            int     21h
  371.  
  372. ; display information if Soundblaster is not on this baseaddress
  373. RESET_ERROR:
  374.            mov     dx,offset sberror
  375.            mov     ah,9
  376.            int     21h                         ; text output
  377.            jmp     return2dos
  378.  
  379. ;──────────────────────────────────────────────────────────────────────────────
  380. ; Our own IRQ for detecting buffer half SB currently plays
  381. ; It's generated by the SoundBlaster hardware
  382. ;──────────────────────────────────────────────────────────────────────────────
  383. OWN_IRQ:
  384.            pusha
  385.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  386.            in      al,dx
  387.            mov     ax,@data
  388.            mov     ds,ax
  389.            mov     dx,offset txtpart0
  390.            cmp     [part],0
  391.            je      notpart1
  392.            mov     dx,offset txtpart1
  393. notpart1:  mov     ah,9
  394.            int     21h             ; text output
  395.            neg     [part]
  396.            inc     [part]          ; part = 1-part  result : 0,1,0,1,0,....
  397.            mov     al,020h
  398.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT
  399.            popa
  400.            IRET
  401.  
  402. END     __start
  403.